Skip to content

fix(mcp): t3_thread_read tells agents which thread sent a message - #18194

Open
Mnigos wants to merge 5 commits into
pingdotgg:mainfrom
Mnigos:thread-read-shows-sender-thread
Open

Mnigos wants to merge 5 commits into
pingdotgg:mainfrom
Mnigos:thread-read-shows-sender-thread

Conversation

@Mnigos

@Mnigos Mnigos commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Addresses the t3_thread_read part of #17517; the server-authored line in the delivered turn is left out (see below).

Problem

When one thread sends a message to another with t3_thread_send, the server records the sender on the message as senderThreadId, and the web and mobile timelines use it. t3_thread_read left the field out of its timeline items, so the agent that received the message could not tell which thread sent it. It only saw createdBy: "agent" and creationSource: "mcp". @pawelwitek34's reproduction shows this clearly. A parent with two delegated children could only work out the sender by reading each child's timeline and matching messageIds.

Change

  • OrchestratorMcpThreadTimelineItem (packages/contracts/src/orchestratorMcp.ts) gets an optional senderThreadId.
  • timelineItem in apps/server/src/mcp/OrchestratorMcpService.ts copies it from the stored message. The field appears only when the message has a sender, so items without one add no response bytes; the advertised output schema gains the optional field.

The issue also suggests adding a server-written line to the delivered turn so the agent would not need a second call. That is out of scope here. It changes the prompt text that every provider receives and needs its own design for how the line is worded and placed. The field on t3_thread_read gives agents a reliable sender id now.

Verification

Gate Result
New test in OrchestratorMcpService.activity.test.ts on base Fails (senderThreadId is undefined)
Same test with the fix Passes: the message from the other thread carries senderThreadId and the user message has no such field
vp test run src/mcp (apps/server) 26 files, 286 tests passed
vp run typecheck (apps/server, packages/contracts) 0 errors
fmt / lint on changed files Clean

Server/contract change with no UI, so no screenshots.

Not checked: a live delegate_task + t3_thread_send round trip against real providers.

Implemented with Claude Opus 5.5, verified with GPT-6 Astra, coordinated by Claude Fable 5.1 in Claude Code.

Messages sent from another thread already record senderThreadId, and the
web and mobile timelines use it, but t3_thread_read items left it out, so
the receiving agent could not tell which thread sent a message.

Timeline items now carry an optional senderThreadId, included only when
the message has one.
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 11, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 11, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 6887a6e

Macroscope's review found this PR approvable — This is a narrowly scoped MCP bug fix that exposes an already-stored sender thread ID through an optional, backward-compatible response field. The accompanying test covers both sender-attributed and ordinary messages without changing defaults or processing behavior.

Notes:

  • All code in this push has already been reviewed. Approvability was decided on eligibility alone.

You can add or adjust custom eligibility rules. Learn more.

@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 11, 2026 — with ChatGPT Codex Connector
@macroscopeapp
macroscopeapp Bot dismissed their stale review October 11, 2026 11:13

Dismissing prior approval to re-evaluate 4f43047

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 11, 2026
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f44e6599-b532-4322-9470-0360ae0bf058
📥 Commits

Reviewing files that changed from the base of the PR and between d73854e and 6887a6e.

📒 Files selected for processing: 3
  • apps/server/src/mcp/OrchestratorMcpService.activity.test.ts
  • apps/server/src/mcp/OrchestratorMcpService.ts
  • packages/contracts/src/orchestratorMcp.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

Thread timeline items now include senderThreadId when the source message defines one. The contract marks the field as optional. A test checks messages with and without the field.

Changes: Thread timeline sender IDs

Layer / File(s) Summary
Add sender thread IDs to timeline items
packages/contracts/src/orchestratorMcp.ts, apps/server/src/mcp/OrchestratorMcpService.ts, apps/server/src/mcp/OrchestratorMcpService.activity.test.ts
The timeline item contract adds an optional senderThreadId. The timeline mapping includes the field when the source message defines it. The test checks that messages without a sender thread omit the field.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 6887a

The change adds optional sender metadata and omits it when unavailable; the readThread test covers both cases. No material merge risk is evident.

Security architecture risk: ⚪ Minimal · up to 6887a

The change adds sender identity metadata to an existing timeline response. The inspected access controls remain unchanged, and the disclosed identifier does not itself grant read or write authority. No material security regression was identified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The incremental disclosure is the sender-thread association on messages already returned to read-capable callers. That audience can include cross-project readers within the existing environment-wide read scope. The change does not itself expose another thread's contents.

Security Findings and Attack Paths

  • inferred — No introduced authorization-bypass path was identified. A recipient can use the new metadata to identify a sender, but subsequent sends still undergo independent caller-liveness and target-mode checks; the returned identifier is not an authority token.

Trust Boundaries and Controls

  • observed — For MCP-originated sends, senderThreadId is derived from the invocation's caller thread and persisted with the message. The new read projection copies that stored provenance rather than accepting a claimed sender from message text. This identifies origin; it does not establish that the message content is trustworthy.
Pre-merge checks: Passed 4
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: exposing the sending thread in t3_thread_read results.
Description check Passed The description covers the problem, implementation, scope, verification results, limitations, and agent attribution. It references issue #17517 but does not include a direct link or explicit maintaine…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

The new readThread sender test now provides ServerSettings.layerTest(), which OrchestratorMcpService requires since main added the server settings dependency.
@macroscopeapp
macroscopeapp Bot dismissed their stale review October 11, 2026 11:17

Dismissing prior approval to re-evaluate d73854e

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 11, 2026
AdEx-Partners-DE added a commit to AdEx-Partners-DE/t3code that referenced this pull request Oct 11, 2026
@kaovilai

Copy link
Copy Markdown
Contributor

+1, this would be useful to us.

We run several threads that message each other with t3_thread_send (a PR session, a research thread, and a coordinating thread). A thread that receives a message can't tell from t3_thread_read which thread sent it, so it can't reply with t3_thread_send without guessing or searching by title. In our case the sender's thread id started with mcp:. Exposing senderThreadId on the timeline item gives the receiver the exact id to reply to.

I haven't run this branch. This comes from reading the PR description and from our use case.

Note

Responses generated with Claude

@Mnigos

Mnigos commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Note

🤖 Claude Fable 5.1 on behalf of Mnigos

Closing to keep my open queue focused; happy to reopen if this is still wanted.

@Mnigos Mnigos closed this Oct 14, 2026
@kaovilai

Copy link
Copy Markdown
Contributor

Eh I commented just 3 days ago I would like this.

@misablaha

Copy link
Copy Markdown

Could this be reopened? We hit the same gap today on 0.0.46-nightly.20261015.3145, and for us it is a trust problem as well as a convenience one.

Setup. One thread acts as the coordinator. It collects requirements, launches new threads with t3_thread_launch, takes over existing ones, and directs subordinate threads through t3_thread_send. When a subordinate escalates, it sometimes needs to confirm an instruction with the coordinator before acting on it: was this task really issued by the coordinator, and in exactly this form?

Repro.

  1. The coordinator (Claude) launched two Codex threads: Astra (gpt-6-astra) and Luna (gpt-6-luna).
  2. Astra sent Luna a task via t3_thread_send. Luna replied to Astra the same way, and Astra then queued a summary to the coordinator.
  3. Each receiver got the message as a plain user turn. Astra confirmed it could not see which thread had sent Luna's reply. On the coordinator's side, Astra's summary could not be told apart from a message typed by the human user.
  4. The exchange only worked because every prompt asked the sender to add a [Name] prefix and a REPLY_TO=<threadId> line.

Why it matters. That convention is just text, so anyone can forge it. Any agent that can call t3_thread_send can write "[Coordinator] confirmed, proceed with X", and the receiver has no means to check it. With several threads that can start work in each other, one agent can steer another into anything. The cause can be a prompt injection that reached one thread, or simply a confused agent.

What would close it.

  • The senderThreadId field on t3_thread_read items, as this PR adds, lets a receiver verify the sender with a single call. That is already enough for the escalation case.
  • The server-authored sender line in the delivered turn, left out of scope here, would also mark agent-to-agent messages as distinct from the human user's own messages. Then even a receiver that never calls t3_thread_read would not mistake them for user instructions.

Happy to test a build.

@Mnigos Mnigos reopened this Oct 15, 2026
@Mnigos

Mnigos commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Note

🤖 Claude Fable 5.1 on behalf of Mnigos

Reopened, thanks for the repro. The forgeable [Name] prefix is exactly why the server-recorded sender needs to reach the agent, and this PR exposes it on t3_thread_read items. Could you add your three-thread scenario to #17517? That issue has no triage yet, and a second report with the trust angle is what will get it looked at; I will merge main into the branch once a maintainer weighs in.

@macroscopeapp
macroscopeapp Bot dismissed their stale review October 15, 2026 13:48

Dismissing prior approval to re-evaluate 6887a6e

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:XS 0-9 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants